package mylittleinvestigations;

import mylittleinvestigations.CaseInformation.CaseXmlWriter;
import mylittleinvestigations.CaseInformation.Case;
import mylittleinvestigations.CaseInformation.CaseXmlReader;
import mylittleinvestigations.CaseInformation.CaseParseException;
import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.newdawn.slick.*;

public class Interrogation extends Conversation
{
    public Interrogation()
    {
        super();
    }

    protected boolean getCanExit()
    {
        return true;
    }

    public void BeginInterrogationRepeat()
    {
        this.actionList.add(new BeginInterrogationRepeatAction());
    }

    public void ShowInterrogation(CharacterPosition speakerPosition, String characterId, String rawDialog, String filePath, int leadInTime) throws CaseParseException
    {
        this.actionList.add(new ShowInterrogationAction(speakerPosition, characterId, rawDialog, filePath, leadInTime));
    }

    public void ShowInterrogationConditional(String conditionFlag, CharacterPosition speakerPosition, String characterId, String rawDialog, String filePath, int leadInTime) throws CaseParseException
    {
        this.actionList.add(new ShowInterrogationAction(conditionFlag, speakerPosition, characterId, rawDialog, filePath, leadInTime));
    }

    public void BeginPressForInfo() throws CaseParseException
    {
        int index = this.actionList.size();
        ShowInterrogationAction showInterrogationAction = null;

        for (int i = index - 1; showInterrogationAction == null && i >= 0; i--)
        {
            Action action = this.actionList.get(i);

            showInterrogationAction = Utils.as(action, ShowInterrogationAction.class);
        }

        if (showInterrogationAction != null)
        {
            showInterrogationAction.setPressForInfoIndex(index);
        }
        else
        {
            throw new CaseParseException("Cannot add BeginPressForInfo without having a containing ShowInterrogation.");
        }

        this.actionList.add(new BeginPressForInfoAction());
    }

    public void EndPressForInfo()
    {
        this.actionList.add(new EndPressForInfoAction());
    }

    public void BeginPresentEvidence(String evidenceId) throws CaseParseException
    {
        int index = this.actionList.size();
        ShowInterrogationAction showInterrogationAction = null;

        for (int i = index - 1; showInterrogationAction == null && i >= 0; i--)
        {
            Action action = this.actionList.get(i);

            showInterrogationAction = Utils.as(action, ShowInterrogationAction.class);
        }

        if (showInterrogationAction != null)
        {
            showInterrogationAction.getEvidencePresentedIndexes().put(evidenceId, index);
        }
        else
        {
            throw new CaseParseException("Cannot add BeginPresentEvidence without having a containing ShowInterrogation .");
        }

        this.actionList.add(new BeginPresentEvidenceAction());
    }

    public void EndPresentEvidence()
    {
        this.actionList.add(new EndPresentEvidenceAction());
    }

    public void BeginUsePartner(String partnerId) throws CaseParseException
    {
        int index = this.actionList.size();
        ShowInterrogationAction showInterrogationAction = null;

        for (int i = index - 1; showInterrogationAction == null && i >= 0; i--)
        {
            Action action = this.actionList.get(i);

            showInterrogationAction = Utils.as(action, ShowInterrogationAction.class);
        }

        if (showInterrogationAction != null)
        {
            showInterrogationAction.getPartnerUsedIndexes().put(partnerId, index);
        }
        else
        {
            throw new CaseParseException("Cannot add BeginUsePartner without having a containing ShowInterrogation .");
        }

        this.actionList.add(new BeginUsePartnerAction());
    }

    public void EndUsePartner()
    {
        this.actionList.add(new EndUsePartnerAction());
    }

    public void EndShowInterrogationBranches()
    {
        int index = this.actionList.size();
        EndPressForInfoAction endPressForInfoAction = null;
        EndPresentEvidenceAction endPresentEvidenceAction = null;
        EndUsePartnerAction endUsePartnerAction = null;

        for (int i = index - 1; !ShowInterrogationAction.class.isInstance(this.actionList.get(i)) && i >= 0; i--)
        {
            Action action = this.actionList.get(i);

            endPressForInfoAction = Utils.as(action, EndPressForInfoAction.class);
            endPresentEvidenceAction = Utils.as(action, EndPresentEvidenceAction.class);
            endUsePartnerAction = Utils.as(action, EndUsePartnerAction.class);

            if (endPressForInfoAction != null)
            {
                endPressForInfoAction.setNextDialogIndex(index);
            }

            if (endPresentEvidenceAction != null)
            {
                endPresentEvidenceAction.setNextDialogIndex(index);
            }

            if (endUsePartnerAction != null)
            {
                endUsePartnerAction.setNextDialogIndex(index);
            }
        }

        this.actionList.add(new EndShowInterrogationBranchesAction());
    }

    public void ExitInterrogationRepeat()
    {
        this.actionList.add(new ExitInterrogationRepeatAction());
    }

    public void BeginWrongPartnerUsed()
    {
        this.actionList.add(new SkipWrongPartnerUsedAction());
        this.actionList.add(new BeginWrongPartnerUsedAction());
    }

    public void EndWrongPartnerUsed()
    {
        this.actionList.add(new EndWrongPartnerUsedAction());
    }

    public void EndInterrogationRepeat() throws CaseParseException
    {
        int index = this.actionList.size();
        EndInterrogationRepeatAction endInterrogationRepeatAction = new EndInterrogationRepeatAction();
        BeginWrongEvidencePresentedAction beginWrongEvidencePresentedAction = null;
        int beginWrongEvidencePresentedActionIndex = -1;
        EndWrongEvidencePresentedAction endWrongEvidencePresentedAction = null;
        int endWrongEvidencePresentedActionIndex = -1;
        BeginWrongPartnerUsedAction beginWrongPartnerUsedAction = null;
        int beginWrongPartnerUsedActionIndex = -1;
        EndWrongPartnerUsedAction endWrongPartnerUsedAction = null;
        int endWrongPartnerUsedActionIndex = -1;
        BeginEndRequestedAction beginEndRequestedAction = null;
        int beginEndRequestedActionIndex = -1;
        EndEndRequestedAction endEndRequestedAction = null;
        int endEndRequestedActionIndex = -1;
        BeginInterrogationRepeatAction beginInterrogationRepeatAction = null;
        int beginInterrogationRepeatActionIndex = -1;
        ShowInterrogationAction previousShowInterrogationAction = null;
        int previousShowInterrogationActionIndex = -1;

        for (int i = index - 1; beginWrongEvidencePresentedAction == null && i >= 0; i--)
        {
            Action action = this.actionList.get(i);

            if (endWrongEvidencePresentedAction == null)
            {
                endWrongEvidencePresentedAction = Utils.as(action, EndWrongEvidencePresentedAction.class);

                if (endWrongEvidencePresentedAction != null)
                {
                    endWrongEvidencePresentedActionIndex = i;
                }
            }

            beginWrongEvidencePresentedAction = Utils.as(action, BeginWrongEvidencePresentedAction.class);
            beginWrongEvidencePresentedActionIndex = i;
        }

        for (int i = index - 1; beginWrongPartnerUsedAction == null && i >= 0; i--)
        {
            Action action = this.actionList.get(i);

            if (endWrongPartnerUsedAction == null)
            {
                endWrongPartnerUsedAction = Utils.as(action, EndWrongPartnerUsedAction.class);

                if (endWrongPartnerUsedAction != null)
                {
                    endWrongPartnerUsedActionIndex = i;
                }
            }

            beginWrongPartnerUsedAction = Utils.as(action, BeginWrongPartnerUsedAction.class);
            beginWrongPartnerUsedActionIndex = i;
        }

        if (this.getCanExit())
        {
            for (int i = index - 1; beginEndRequestedAction == null && i >= 0; i--)
            {
                Action action = this.actionList.get(i);

                if (endEndRequestedAction == null)
                {
                    endEndRequestedAction = Utils.as(action, EndEndRequestedAction.class);

                    if (endEndRequestedAction != null)
                    {
                        endEndRequestedActionIndex = i;
                    }
                }

                beginEndRequestedAction = Utils.as(action, BeginEndRequestedAction.class);
                beginEndRequestedActionIndex = i;
            }
        }

        if (beginWrongEvidencePresentedAction == null || endWrongEvidencePresentedAction == null)
        {
            throw new CaseParseException("Every interrogation must have WrongEvidencePresented appear within it.");
        }

        if (this.getCanExit() && (beginEndRequestedAction == null || endEndRequestedAction == null))
        {
            throw new CaseParseException("Every interrogation that can be exited must have BeginInterrogationEnded appear within it.");
        }

        for (int i = index - 1; beginInterrogationRepeatAction == null && i >= 0; i--)
        {
            Action action = this.actionList.get(i);

            beginInterrogationRepeatAction = Utils.as(action, BeginInterrogationRepeatAction.class);

            if (beginInterrogationRepeatAction != null)
            {
                beginInterrogationRepeatActionIndex = i;
            }

            ShowInterrogationAction showInterrogationAction = Utils.as(action, ShowInterrogationAction.class);

            if (showInterrogationAction != null)
            {
                showInterrogationAction.setWrongEvidencePresentedIndex(beginWrongEvidencePresentedActionIndex);
                showInterrogationAction.setWrongPartnerUsedIndex(beginWrongPartnerUsedActionIndex);
                showInterrogationAction.setEndRequestedIndex(beginEndRequestedActionIndex);

                if (previousShowInterrogationAction != null)
                {
                    previousShowInterrogationAction.setPreviousInterrogationIndex(i);
                    showInterrogationAction.setNextInterrogationIndex(previousShowInterrogationActionIndex);
                }
                else
                {
                    for (int j = i + 1; j < index - 1; j++)
                    {
                        EndShowInterrogationBranchesAction endShowInterrogationBranchesAction = Utils.as(this.actionList.get(j), EndShowInterrogationBranchesAction.class);

                        if (endShowInterrogationBranchesAction != null)
                        {
                            showInterrogationAction.setInterrogationFinishIndex(j);
                            break;
                        }
                    }
                }

                previousShowInterrogationAction = showInterrogationAction;
                previousShowInterrogationActionIndex = i;
            }

            ExitInterrogationRepeatAction completeInterrogationAction = Utils.as(action, ExitInterrogationRepeatAction.class);

            if (completeInterrogationAction != null)
            {
                // We'll assume there will be something after the interrogation if we're seeing this.
                completeInterrogationAction.setAfterEndInterrogationRepeatIndex(index + 1);
            }

            SkipWrongEvidencePresentedAction skipWrongEvidencePresentedAction = Utils.as(action, SkipWrongEvidencePresentedAction.class);

            if (skipWrongEvidencePresentedAction != null)
            {
                skipWrongEvidencePresentedAction.setAfterWrongEvidencePresentedIndex(endWrongEvidencePresentedActionIndex + 1);
            }

            SkipWrongPartnerUsedAction skipWrongPartnerUsedAction = Utils.as(action, SkipWrongPartnerUsedAction.class);

            if (skipWrongPartnerUsedAction != null)
            {
                skipWrongPartnerUsedAction.setAfterWrongPartnerUsedIndex(endWrongPartnerUsedActionIndex + 1);
            }

            SkipEndRequestedAction skipEndRequestedAction = Utils.as(action, SkipEndRequestedAction.class);

            if (skipEndRequestedAction != null)
            {
                skipEndRequestedAction.setAfterEndRequestedIndex(endEndRequestedActionIndex + 1);
            }
        }

        if (beginInterrogationRepeatAction != null)
        {
            endInterrogationRepeatAction.setBeginInterrogationRepeatIndex(beginInterrogationRepeatActionIndex);
        }
        else
        {
            throw new CaseParseException("Cannot add EndInterrogationRepeat without having BeginInterrogationRepeat before it.");
        }

        this.actionList.add(endInterrogationRepeatAction);
    }

    @Override
    public void SaveToXml(CaseXmlWriter writer) throws IOException
    {
        writer.StartElement("Interrogation");
        this.SaveToXmlCore(writer);
        writer.EndElement();
    }
    
    public Interrogation(CaseXmlReader reader)
    {
        this.Initialize();
        reader.StartElement("Interrogation");
        this.LoadFromXmlCore(reader);
        reader.EndElement();
    }
    
    @Override
    protected Action GetActionForNextElement(CaseXmlReader reader)
    {
        if (reader.ElementExists("BeginInterrogationRepeatAction"))
        {
            return new BeginInterrogationRepeatAction(reader);
        }
        else if (reader.ElementExists("ShowInterrogationAction"))
        {
            return new ShowInterrogationAction(reader);
        }
        else if (reader.ElementExists("BeginPressForInfoAction"))
        {
            return new BeginPressForInfoAction(reader);
        }
        else if (reader.ElementExists("EndPressForInfoAction"))
        {
            return new EndPressForInfoAction(reader);
        }
        else if (reader.ElementExists("BeginPresentEvidenceAction"))
        {
            return new BeginPresentEvidenceAction(reader);
        }
        else if (reader.ElementExists("EndPresentEvidenceAction"))
        {
            return new EndPresentEvidenceAction(reader);
        }
        else if (reader.ElementExists("BeginUsePartnerAction"))
        {
            return new BeginUsePartnerAction(reader);
        }
        else if (reader.ElementExists("EndUsePartnerAction"))
        {
            return new EndUsePartnerAction(reader);
        }
        else if (reader.ElementExists("EndShowInterrogationBranchesAction"))
        {
            return new EndShowInterrogationBranchesAction(reader);
        }
        else if (reader.ElementExists("ExitInterrogationRepeatAction"))
        {
            return new ExitInterrogationRepeatAction(reader);
        }
        else if (reader.ElementExists("SkipWrongPartnerUsedAction"))
        {
            return new SkipWrongPartnerUsedAction(reader);
        }
        else if (reader.ElementExists("BeginWrongPartnerUsedAction"))
        {
            return new BeginWrongPartnerUsedAction(reader);
        }
        else if (reader.ElementExists("EndWrongPartnerUsedAction"))
        {
            return new EndWrongPartnerUsedAction(reader);
        }
        else if (reader.ElementExists("EndInterrogationRepeatAction"))
        {
            return new EndInterrogationRepeatAction(reader);
        }
        else
        {
            return super.GetActionForNextElement(reader);
        }
    }

    protected class BeginInterrogationRepeatAction extends SingleAction
    {
        State cachedState;

        public BeginInterrogationRepeatAction()
        {
            this.cachedState = null;
        }

        @Override
        public void Execute(State state)
        {
            if (this.cachedState == null)
            {
                this.cachedState = state.CreateCachedState();
            }
            else
            {
                state.RestoreFromState(this.cachedState);
            }
        }
        
        @Override
        public void SaveToXml(CaseXmlWriter writer) throws IOException
        {
            writer.StartElement("BeginInterrogationRepeatAction");
            writer.EndElement();
        }

        private BeginInterrogationRepeatAction(CaseXmlReader reader)
        {
            reader.StartElement("BeginInterrogationRepeatAction");
            reader.EndElement();
        }
        
        protected Action CloneWithOffset(int indexOffset)
        {
            BeginInterrogationRepeatAction action = new BeginInterrogationRepeatAction();
            
            return action;
        }
    }

    protected class ShowInterrogationAction extends ShowDialogAction
    {
        private boolean nextIndexSet;
        State cachedState;
        private String conditionFlag;

        public ShowInterrogationAction()
        {
        }

        public ShowInterrogationAction(CharacterPosition speakerPosition, String characterId, String rawDialog, String filePath, int leadInTime) throws CaseParseException
        {
            this(null, speakerPosition, characterId, rawDialog, filePath, leadInTime);
        }

        public ShowInterrogationAction(String conditionFlag, CharacterPosition speakerPosition, String characterId, String rawDialog, String filePath, int leadInTime) throws CaseParseException
        {
            super(speakerPosition, characterId, rawDialog, filePath, leadInTime);
            this.evidencePresentedIndexes = new HashMap<String, Integer>();
            this.partnerUsedIndexes = new HashMap<String, Integer>();
            this.nextIndexSet = false;
            this.cachedState = null;
            this.conditionFlag = conditionFlag;

            this.previousInterrogationIndex = -1;
            this.nextInterrogationIndex = -1;
            this.interrogationFinishIndex = -1;
            this.pressForInfoIndex = -1;
            this.wrongEvidencePresentedIndex = -1;
            this.wrongPartnerUsedIndex = -1;
        }

        @Override
        protected boolean getIsInterrogation()
        {
            return true;
        }

        @Override
        protected boolean getCanNavigateBack()
        {
            return this.getPreviousInterrogationIndex() >= 0;
        }

        @Override
        protected boolean getCanNavigateForward()
        {
            return this.getNextInterrogationIndex() >= 0;
        }

        @Override
        public boolean getShouldSkip()
        {
            return this.conditionFlag != null && !Case.getInstance().getFlagManager().IsFlagSet(this.conditionFlag);
        }
        
        private int previousInterrogationIndex;
        
        public int getPreviousInterrogationIndex() { return this.previousInterrogationIndex; }
        public void setPreviousInterrogationIndex(int previousInterrogationIndex) { this.previousInterrogationIndex = previousInterrogationIndex; }
        
        private int nextInterrogationIndex;
        
        public int getNextInterrogationIndex() { return this.nextInterrogationIndex; }
        public void setNextInterrogationIndex(int nextInterrogationIndex) { this.nextInterrogationIndex = nextInterrogationIndex; }
        
        private int interrogationFinishIndex;

        public int getInterrogationFinishIndex() { return this.interrogationFinishIndex; }
        public void setInterrogationFinishIndex(int interrogationFinishIndex) { this.interrogationFinishIndex = interrogationFinishIndex; }
        
        private int pressForInfoIndex;

        public int getPressForInfoIndex() { return this.pressForInfoIndex; }
        public void setPressForInfoIndex(int pressForInfoIndex) { this.pressForInfoIndex = pressForInfoIndex; }
        
        protected HashMap<String, Integer> evidencePresentedIndexes;

        public HashMap<String, Integer> getEvidencePresentedIndexes() { return this.evidencePresentedIndexes; }
        public void setEvidencePresentedIndexes(HashMap<String, Integer> evidencePresentedIndexes) { this.evidencePresentedIndexes = evidencePresentedIndexes; }
        
        protected HashMap<String, Integer> partnerUsedIndexes;

        public HashMap<String, Integer> getPartnerUsedIndexes() { return this.partnerUsedIndexes; }
        public void setPartnerUsedIndexes(HashMap<String, Integer> partnerUsedIndexes) { this.partnerUsedIndexes = partnerUsedIndexes; }

        private int wrongEvidencePresentedIndex;
        
        public int getWrongEvidencePresentedIndex() { return this.wrongEvidencePresentedIndex; }
        public void setWrongEvidencePresentedIndex(int wrongEvidencePresentedIndex) { this.wrongEvidencePresentedIndex = wrongEvidencePresentedIndex; }

        private int wrongPartnerUsedIndex;
        
        public int getWrongPartnerUsedIndex() { return this.wrongPartnerUsedIndex; }
        public void setWrongPartnerUsedIndex(int wrongPartnerUsedIndex) { this.wrongPartnerUsedIndex = wrongPartnerUsedIndex; }
        
        private int endRequestedIndex;
        
        public int getEndRequestedIndex() { return this.endRequestedIndex; }
        public void setEndRequestedIndex(int endRequestedIndex) { this.endRequestedIndex = endRequestedIndex; }

        @Override
        public void GoToNext(State state)
        {
            if (state.getLastNavigationDirection() == DirectNavigationDirection.Back)
            {
                state.setActionIndex(this.getPreviousInterrogationIndex());
            }
            else
            {
                state.setActionIndex(this.getNextInterrogationIndex() >= 0 ? this.getNextInterrogationIndex() : this.getInterrogationFinishIndex());
            }
        }

        @Override
        public void Begin(State state) throws CaseParseException
        {
            super.Begin(state);

            this.nextIndexSet = false;

            state.RestoreCachedState();

            if (this.cachedState == null)
            {
                this.cachedState = state.CreateCachedState();
            }
            else
            {
                state.RestoreFromState(this.cachedState);
            }

            // If we directly navigated here in an interrogation,
            // we should just immediately display all of the text.
            if (state.getLastNavigationDirection() != DirectNavigationDirection.None)
            {
                this.dialog.Finish();
                state.setLastNavigationDirection(DirectNavigationDirection.None);
            }
        }

        @Override
        public void Update(GameContainer gc, int delta)
        {
            super.Update(gc, delta);

            if (this.getIsFinished() && !this.nextIndexSet)
            {
                int nextIndex = -1;

                if (this.getNextInterrogationIndex() >= 0)
                {
                    nextIndex = this.getNextInterrogationIndex();
                }
                else if (this.getInterrogationFinishIndex() >= 0)
                {
                    nextIndex = this.getInterrogationFinishIndex();
                }
                else
                {
                    throw new IllegalStateException("At least one of NextInterrogationIndex and InterrogationFinishIndex must be set.");
                }

                // If we're done and the next index hasn't been set yet,
                // set it to the next dialog index.
                this.state.setActionIndex(nextIndex);
                this.nextIndexSet = true;
            }
        }

        @Override
        public void Draw(GameContainer gc, Graphics g, float yOffset) throws SlickException
        {
            super.Draw(gc, g, yOffset);
        }

        @Override
        public void Reset()
        {
            super.Reset();

            this.nextIndexSet = false;

            state.CacheState();
        }

        @Override
        public void OnDialogDirectlyNavigated(Dialog sender, DirectNavigationDirection direction)
        {
            if (sender != this.dialog)
            {
                return;
            }
            
            if (direction == DirectNavigationDirection.Back)
            {
                if (this.getPreviousInterrogationIndex() < 0)
                {
                    throw new IllegalStateException("Cannot navigate back when there is nowhere to navigate back to.");
                }

                this.state.setActionIndex(this.getPreviousInterrogationIndex());
                this.nextIndexSet = true;
                this.setIsFinished(true);
            }
            else if (direction == DirectNavigationDirection.Forward)
            {
                this.state.setActionIndex(this.getNextInterrogationIndex());
                this.nextIndexSet = true;
                this.setIsFinished(true);
            }

            this.state.setLastNavigationDirection(direction);
        }

        @Override
        public void OnDialogPressForInfoClicked(Dialog sender)
        {
            if (sender != this.dialog)
            {
                return;
            }
            
            this.state.setActionIndex(this.getPressForInfoIndex());
            this.nextIndexSet = true;
            this.setIsFinished(true);
        }

        @Override
        public void OnDialogEvidencePresented(Dialog sender, String evidenceId)
        {
            if (sender != this.dialog)
            {
                return;
            }
            
            if (this.getEvidencePresentedIndexes().containsKey(evidenceId))
            {
                this.state.setActionIndex(this.getEvidencePresentedIndexes().get(evidenceId));
            }
            else
            {
                this.state.setActionIndex(this.getWrongEvidencePresentedIndex());
            }

            this.nextIndexSet = true;
            this.setIsFinished(true);
        }

        @Override
        public void OnDialogUsePartner(Dialog sender)
        {
            if (sender != this.dialog)
            {
                return;
            }
            
            if (this.getPartnerUsedIndexes().containsKey(Case.getInstance().getPartnerManager().GetCurrentPartnerId()))
            {
                this.state.setActionIndex(this.getPartnerUsedIndexes().get(Case.getInstance().getPartnerManager().GetCurrentPartnerId()));
            }
            else
            {
                this.state.setActionIndex(this.getWrongPartnerUsedIndex());
            }

            this.nextIndexSet = true;
            this.setIsFinished(true);
        }

        @Override
        public void OnDialogEndRequested(Dialog sender)
        {
            if (sender != this.dialog)
            {
                return;
            }
            
            this.state.setActionIndex(this.getEndRequestedIndex());

            this.nextIndexSet = true;
            this.setIsFinished(true);
        }
        
        @Override
        public void SaveToXml(CaseXmlWriter writer) throws IOException
        {
            writer.StartElement("ShowInterrogationAction");
            this.SaveToXmlCore(writer);
            writer.EndElement();
        }
        
        @Override
        protected void SaveToXmlCore(CaseXmlWriter writer) throws IOException
        {
            super.SaveToXmlCore(writer);
            writer.StartElement("EvidencePresentedIndexes");
            String[] evidenceIds = this.evidencePresentedIndexes.keySet().toArray(new String[0]);
            
            for (int i = 0; i < evidenceIds.length; i++)
            {
                writer.StartElement("Entry");
                String evidenceId = evidenceIds[i];
                writer.WriteTextElement("EvidenceId", evidenceId);
                writer.WriteIntElement("Index", this.evidencePresentedIndexes.get(evidenceId));
                writer.EndElement();
            }
            
            writer.EndElement();
            
            writer.StartElement("PartnerUsedIndexes");
            String[] partnerIds = this.partnerUsedIndexes.keySet().toArray(new String[0]);
            
            for (int i = 0; i < partnerIds.length; i++)
            {
                writer.StartElement("Entry");
                String partnerId = partnerIds[i];
                writer.WriteTextElement("PartnerId", partnerId);
                writer.WriteIntElement("Index", this.partnerUsedIndexes.get(partnerId));
                writer.EndElement();
            }
            
            writer.EndElement();
            
            if (this.conditionFlag != null)
            {
                writer.WriteTextElement("ConditionFlag", this.conditionFlag);
            }
            
            writer.WriteIntElement("PreviousInterrogationIndex", this.previousInterrogationIndex);
            writer.WriteIntElement("NextInterrogationIndex", this.nextInterrogationIndex);
            writer.WriteIntElement("InterrogationFinishIndex", this.interrogationFinishIndex);
            writer.WriteIntElement("PressForInfoIndex", this.pressForInfoIndex);
            writer.WriteIntElement("WrongEvidencePresentedIndex", this.wrongEvidencePresentedIndex);
            writer.WriteIntElement("WrongPartnerUsedIndex", this.wrongPartnerUsedIndex);
            writer.WriteIntElement("EndRequestedIndex", this.endRequestedIndex);
        }

        private ShowInterrogationAction(CaseXmlReader reader)
        {
            this.evidencePresentedIndexes = new HashMap<String, Integer>();
            this.partnerUsedIndexes = new HashMap<String, Integer>();
            
            reader.StartElement("ShowInterrogationAction");
            this.LoadFromXmlCore(reader);
            reader.EndElement();
        }
        
        @Override
        protected void LoadFromXmlCore(CaseXmlReader reader)
        {
            super.LoadFromXmlCore(reader);
            reader.StartElement("EvidencePresentedIndexes");
            reader.StartList("Entry");
            
            while (reader.MoveToNextListItem())
            {
                String evidenceId = reader.ReadTextElement("EvidenceId");
                int index = reader.ReadIntElement("Index");
                this.evidencePresentedIndexes.put(evidenceId, index);
            }
            
            reader.EndElement();
            
            reader.StartElement("PartnerUsedIndexes");
            reader.StartList("Entry");
            
            while (reader.MoveToNextListItem())
            {
                String partnerId = reader.ReadTextElement("PartnerId");
                int index = reader.ReadIntElement("Index");
                this.partnerUsedIndexes.put(partnerId, index);
            }
            
            reader.EndElement();
            
            if (reader.ElementExists("ConditionFlag"))
            {
                this.conditionFlag = reader.ReadTextElement("ConditionFlag");
            }
            
            this.previousInterrogationIndex = reader.ReadIntElement("PreviousInterrogationIndex");
            this.nextInterrogationIndex = reader.ReadIntElement("NextInterrogationIndex");
            this.interrogationFinishIndex = reader.ReadIntElement("InterrogationFinishIndex");
            this.pressForInfoIndex = reader.ReadIntElement("PressForInfoIndex");
            this.wrongEvidencePresentedIndex = reader.ReadIntElement("WrongEvidencePresentedIndex");
            this.wrongPartnerUsedIndex = reader.ReadIntElement("WrongPartnerUsedIndex");
            this.endRequestedIndex = reader.ReadIntElement("EndRequestedIndex");
        }
        
        @Override
        protected Action CloneWithOffset(int indexOffset)
        {
            ShowInterrogationAction action = null;
            
            try
            {
                action = new ShowInterrogationAction(this.conditionFlag, this.speakerPosition, this.characterId, this.rawDialog, this.filePath, this.leadInTime);
                action.setEndRequestedIndex(this.endRequestedIndex + indexOffset);
                
                HashMap<String, Integer> clonedEvidencePresentedIndexes = new HashMap<String, Integer>();
                
                for (String key : this.evidencePresentedIndexes.keySet())
                {
                    clonedEvidencePresentedIndexes.put(key, this.evidencePresentedIndexes.get(key) + indexOffset);
                }
                
                action.setEvidencePresentedIndexes(clonedEvidencePresentedIndexes);
                
                action.setInterrogationFinishIndex(this.interrogationFinishIndex + indexOffset);
                action.setIsInConfrontation(this.isInConfrontation);
                action.setNextInterrogationIndex(this.nextInterrogationIndex + indexOffset);
                
                HashMap<String, Integer> clonedPartnerUsedIndexes = new HashMap<String, Integer>();
                
                for (String key : this.partnerUsedIndexes.keySet())
                {
                    clonedEvidencePresentedIndexes.put(key, this.partnerUsedIndexes.get(key) + indexOffset);
                }
                
                action.setPartnerUsedIndexes(clonedPartnerUsedIndexes);
                
                action.setPressForInfoIndex(this.pressForInfoIndex + indexOffset);
                action.setPreviousInterrogationIndex(this.previousInterrogationIndex + indexOffset);
                action.setWrongEvidencePresentedIndex(this.wrongEvidencePresentedIndex + indexOffset);
                action.setWrongPartnerUsedIndex(this.wrongPartnerUsedIndex + indexOffset);
            }
            catch (CaseParseException ex)
            {
                Logger.getLogger(Interrogation.class.getName()).log(Level.SEVERE, null, ex);
            }
            
            return action;
        }
    }

    protected class BeginPressForInfoAction extends SingleAction
    {
        public BeginPressForInfoAction()
        {
        }

        @Override
        public void Execute(State state)
        {
        }
        
        @Override
        public void SaveToXml(CaseXmlWriter writer) throws IOException
        {
            writer.StartElement("BeginPressForInfoAction");
            writer.EndElement();
        }

        private BeginPressForInfoAction(CaseXmlReader reader)
        {
            reader.StartElement("BeginPressForInfoAction");
            reader.EndElement();
        }
        
        protected Action CloneWithOffset(int indexOffset)
        {
            BeginPressForInfoAction action = new BeginPressForInfoAction();
            
            return action;
        }
    }

    protected class EndPressForInfoAction extends SingleAction
    {
        public EndPressForInfoAction()
        {
            this.nextDialogIndex = -1;
        }
        
        private int nextDialogIndex;

        public int getNextDialogIndex() { return this.nextDialogIndex; }
        public void setNextDialogIndex(int nextDialogIndex) { this.nextDialogIndex = nextDialogIndex; }

        @Override
        public void Execute(State state)
        {
            state.setActionIndex(this.getNextDialogIndex());
        }
        
        @Override
        public void SaveToXml(CaseXmlWriter writer) throws IOException
        {
            writer.StartElement("EndPressForInfoAction");
            writer.WriteIntElement("NextDialogIndex", this.nextDialogIndex);
            writer.EndElement();
        }

        private EndPressForInfoAction(CaseXmlReader reader)
        {
            reader.StartElement("EndPressForInfoAction");
            this.nextDialogIndex = reader.ReadIntElement("NextDialogIndex");
            reader.EndElement();
        }
        
        protected Action CloneWithOffset(int indexOffset)
        {
            EndPressForInfoAction action = new EndPressForInfoAction();
            
            action.setNextDialogIndex(this.nextDialogIndex + indexOffset);
            
            return action;
        }
    }

    protected class BeginPresentEvidenceAction extends SingleAction
    {
        public BeginPresentEvidenceAction()
        {
        }

        @Override
        public void Execute(State state)
        {
        }
        
        @Override
        public void SaveToXml(CaseXmlWriter writer) throws IOException
        {
            writer.StartElement("BeginPresentEvidenceAction");
            writer.EndElement();
        }

        private BeginPresentEvidenceAction(CaseXmlReader reader)
        {
            reader.StartElement("BeginPresentEvidenceAction");
            reader.EndElement();
        }
        
        protected Action CloneWithOffset(int indexOffset)
        {
            BeginPresentEvidenceAction action = new BeginPresentEvidenceAction();
            
            return action;
        }
    }

    protected class EndPresentEvidenceAction extends SingleAction
    {
        public EndPresentEvidenceAction()
        {
            this.nextDialogIndex = -1;
        }
        
        private int nextDialogIndex;

        public int getNextDialogIndex() { return this.nextDialogIndex; }
        public void setNextDialogIndex(int nextDialogIndex) { this.nextDialogIndex = nextDialogIndex; }

        @Override
        public void Execute(State state)
        {
            state.setActionIndex(this.getNextDialogIndex());
        }
        
        @Override
        public void SaveToXml(CaseXmlWriter writer) throws IOException
        {
            writer.StartElement("EndPresentEvidenceAction");
            writer.WriteIntElement("NextDialogIndex", this.nextDialogIndex);
            writer.EndElement();
        }

        private EndPresentEvidenceAction(CaseXmlReader reader)
        {
            reader.StartElement("EndPresentEvidenceAction");
            this.nextDialogIndex = reader.ReadIntElement("NextDialogIndex");
            reader.EndElement();
        }
        
        protected Action CloneWithOffset(int indexOffset)
        {
            EndPresentEvidenceAction action = new EndPresentEvidenceAction();
            
            this.setNextDialogIndex(this.nextDialogIndex + indexOffset);
            
            return action;
        }
    }

    protected class BeginUsePartnerAction extends SingleAction
    {
        public BeginUsePartnerAction()
        {
        }

        @Override
        public void Execute(State state)
        {
        }
        
        @Override
        public void SaveToXml(CaseXmlWriter writer) throws IOException
        {
            writer.StartElement("BeginUsePartnerAction");
            writer.EndElement();
        }

        private BeginUsePartnerAction(CaseXmlReader reader)
        {
            reader.StartElement("BeginUsePartnerAction");
            reader.EndElement();
        }
        
        protected Action CloneWithOffset(int indexOffset)
        {
            BeginUsePartnerAction action = new BeginUsePartnerAction();
            
            return action;
        }
    }

    protected class EndUsePartnerAction extends SingleAction
    {
        public EndUsePartnerAction()
        {
            this.nextDialogIndex = -1;
        }
        
        private int nextDialogIndex;

        public int getNextDialogIndex() { return this.nextDialogIndex; }
        public void setNextDialogIndex(int nextDialogIndex) { this.nextDialogIndex = nextDialogIndex; }

        @Override
        public void Execute(State state)
        {
            state.setActionIndex(this.getNextDialogIndex());
        }
        
        @Override
        public void SaveToXml(CaseXmlWriter writer) throws IOException
        {
            writer.StartElement("EndUsePartnerAction");
            writer.WriteIntElement("NextDialogIndex", this.nextDialogIndex);
            writer.EndElement();
        }

        private EndUsePartnerAction(CaseXmlReader reader)
        {
            reader.StartElement("EndUsePartnerAction");
            this.nextDialogIndex = reader.ReadIntElement("NextDialogIndex");
            reader.EndElement();
        }
        
        protected Action CloneWithOffset(int indexOffset)
        {
            EndUsePartnerAction action = new EndUsePartnerAction();
            
            action.setNextDialogIndex(this.nextDialogIndex + indexOffset);
            
            return action;
        }
    }

    protected class EndShowInterrogationBranchesAction extends SingleAction
    {
        public EndShowInterrogationBranchesAction()
        {
        }

        @Override
        public void Execute(State state)
        {
        }
        
        @Override
        public void SaveToXml(CaseXmlWriter writer) throws IOException
        {
            writer.StartElement("EndShowInterrogationBranchesAction");
            writer.EndElement();
        }

        private EndShowInterrogationBranchesAction(CaseXmlReader reader)
        {
            reader.StartElement("EndShowInterrogationBranchesAction");
            reader.EndElement();
        }
        
        protected Action CloneWithOffset(int indexOffset)
        {
            EndShowInterrogationBranchesAction action = new EndShowInterrogationBranchesAction();
            
            return action;
        }
    }

    protected class ExitInterrogationRepeatAction extends SingleAction
    {
        public ExitInterrogationRepeatAction()
        {
            this.afterEndInterrogationRepeatIndex = -1;
        }
        
        private int afterEndInterrogationRepeatIndex;

        public int getAfterEndInterrogationRepeatIndex() { return this.afterEndInterrogationRepeatIndex; }
        public void setAfterEndInterrogationRepeatIndex(int afterEndInterrogationRepeatIndex) { this.afterEndInterrogationRepeatIndex = afterEndInterrogationRepeatIndex; }

        @Override
        public void Execute(State state)
        {
            state.setActionIndex(this.getAfterEndInterrogationRepeatIndex());
        }
        
        @Override
        public void SaveToXml(CaseXmlWriter writer) throws IOException
        {
            writer.StartElement("ExitInterrogationRepeatAction");
            writer.WriteIntElement("AfterEndInterrogationRepeatIndex", this.afterEndInterrogationRepeatIndex);
            writer.EndElement();
        }

        private ExitInterrogationRepeatAction(CaseXmlReader reader)
        {
            reader.StartElement("ExitInterrogationRepeatAction");
            this.afterEndInterrogationRepeatIndex = reader.ReadIntElement("AfterEndInterrogationRepeatIndex");
            reader.EndElement();
        }
        
        protected Action CloneWithOffset(int indexOffset)
        {
            ExitInterrogationRepeatAction action = new ExitInterrogationRepeatAction();
            
            action.setAfterEndInterrogationRepeatIndex(this.afterEndInterrogationRepeatIndex + indexOffset);
            
            return action;
        }
    }

    protected class SkipWrongPartnerUsedAction extends SingleAction
    {
        public SkipWrongPartnerUsedAction()
        {
            this.afterWrongPartnerUsedIndex = -1;
        }
        
        private int afterWrongPartnerUsedIndex;

        public int getAfterWrongPartnerUsedIndex() { return this.afterWrongPartnerUsedIndex; }
        public void setAfterWrongPartnerUsedIndex(int afterWrongPartnerUsedIndex) { this.afterWrongPartnerUsedIndex = afterWrongPartnerUsedIndex; }

        @Override
        public void Execute(State state)
        {
            state.setActionIndex(this.getAfterWrongPartnerUsedIndex());
        }
        
        @Override
        public void SaveToXml(CaseXmlWriter writer) throws IOException
        {
            writer.StartElement("SkipWrongPartnerUsedAction");
            writer.WriteIntElement("AfterWrongPartnerUsedIndex", this.afterWrongPartnerUsedIndex);
            writer.EndElement();
        }

        private SkipWrongPartnerUsedAction(CaseXmlReader reader)
        {
            reader.StartElement("SkipWrongPartnerUsedAction");
            this.afterWrongPartnerUsedIndex = reader.ReadIntElement("AfterWrongPartnerUsedIndex");
            reader.EndElement();
        }
        
        protected Action CloneWithOffset(int indexOffset)
        {
            SkipWrongPartnerUsedAction action = new SkipWrongPartnerUsedAction();
            
            action.setAfterWrongPartnerUsedIndex(this.afterWrongPartnerUsedIndex + indexOffset);
            
            return action;
        }
    }

    protected class BeginWrongPartnerUsedAction extends SingleAction
    {
        public BeginWrongPartnerUsedAction()
        {
        }

        @Override
        public void Execute(State state)
        {
            state.CachePreviousActionIndex();
        }
        
        @Override
        public void SaveToXml(CaseXmlWriter writer) throws IOException
        {
            writer.StartElement("BeginWrongPartnerUsedAction");
            writer.EndElement();
        }

        private BeginWrongPartnerUsedAction(CaseXmlReader reader)
        {
            reader.StartElement("BeginWrongPartnerUsedAction");
            reader.EndElement();
        }
        
        protected Action CloneWithOffset(int indexOffset)
        {
            BeginWrongPartnerUsedAction action = new BeginWrongPartnerUsedAction();
            
            return action;
        }
    }

    protected class EndWrongPartnerUsedAction extends SingleAction
    {
        public EndWrongPartnerUsedAction()
        {
        }

        @Override
        public void Execute(State state)
        {
            state.RestoreActionIndexFromCache();
        }
        
        @Override
        public void SaveToXml(CaseXmlWriter writer) throws IOException
        {
            writer.StartElement("EndWrongPartnerUsedAction");
            writer.EndElement();
        }

        private EndWrongPartnerUsedAction(CaseXmlReader reader)
        {
            reader.StartElement("EndWrongPartnerUsedAction");
            reader.EndElement();
        }
        
        protected Action CloneWithOffset(int indexOffset)
        {
            EndWrongPartnerUsedAction action = new EndWrongPartnerUsedAction();
            
            return action;
        }
    }

    protected class EndInterrogationRepeatAction extends SingleAction
    {
        public EndInterrogationRepeatAction()
        {
            this.beginInterrogationRepeatIndex = -1;
        }
        
        private int beginInterrogationRepeatIndex;

        public int getBeginInterrogationRepeatIndex() { return this.beginInterrogationRepeatIndex; }
        public void setBeginInterrogationRepeatIndex(int beginInterrogationRepeatIndex) { this.beginInterrogationRepeatIndex = beginInterrogationRepeatIndex; }

        @Override
        public void Execute(State state)
        {
            state.setActionIndex(this.getBeginInterrogationRepeatIndex());
        }
        
        @Override
        public void SaveToXml(CaseXmlWriter writer) throws IOException
        {
            writer.StartElement("EndInterrogationRepeatAction");
            writer.WriteIntElement("BeginInterrogationRepeatIndex", this.beginInterrogationRepeatIndex);
            writer.EndElement();
        }

        private EndInterrogationRepeatAction(CaseXmlReader reader) 
        {
            reader.StartElement("EndInterrogationRepeatAction");
            this.beginInterrogationRepeatIndex = reader.ReadIntElement("BeginInterrogationRepeatIndex");
            reader.EndElement();
        }
        
        protected Action CloneWithOffset(int indexOffset)
        {
            EndInterrogationRepeatAction action = new EndInterrogationRepeatAction();
            
            action.setBeginInterrogationRepeatIndex(this.beginInterrogationRepeatIndex + indexOffset);
            
            return action;
        }
    }
}
